/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.ext.echarts.builder.impl;

import cn.easyplatform.lang.Lang;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.utils.resource.GResource;
import cn.easyplatform.utils.resource.Scans;
import cn.easyplatform.web.ext.ComponentHandler;
import cn.easyplatform.web.ext.echarts.ECharts;
import cn.easyplatform.web.ext.echarts.builder.IEChartsBuilder;
import cn.easyplatform.web.ext.echarts.builder.TemplateUnit;
import cn.easyplatform.web.ext.echarts.lib.Dataset;
import cn.easyplatform.web.ext.echarts.lib.Option;
import cn.easyplatform.web.ext.echarts.util.ChartType;
import cn.easyplatform.web.ext.echarts.util.EChartsUtils;
import cn.easyplatform.web.ext.echarts.util.GsonUtil;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.*;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public abstract class AbstractChartsBuilder implements IEChartsBuilder {

    private final static Map<String, java.util.Map<String, TemplateUnit>> RESOURCES = new HashMap<>();

    private static Map<String,List> condition;

    private static void init() {
        try {
            for (ChartType type : ChartType.values()) {
                List<GResource> grList = Scans.me().scan("web/js/echarts/img/" + type.name()+"/");
                Map<String, TemplateUnit> images = new TreeMap<>();
                for (GResource gr : grList) {
                    String filename = FilenameUtils.getBaseName(gr.getName());
                    if (!Strings.isBlank(filename)) {
                        String extName = FilenameUtils.getExtension(gr.getName());
                        TemplateUnit item = images.get(filename);
                        if (item == null)
                            images.put(filename, item = new TemplateUnit());
                        if (extName.equals("jpg") || extName.equals("png"))
                            item.setImage("~./js/echarts/img/" + type.name() + "/" + gr.getName());
                        else if (extName.equals("json"))
                            item.setJson(IOUtils.toString(gr.getInputStream(), "utf-8"));
                        else if (extName.equals("js"))
                            item.setOption(IOUtils.toString(gr.getInputStream(), "utf-8"));
                        item.setName(filename);
                    }
                }
                RESOURCES.put(type.name(), images);
            }
            condition = EChartsUtils.getComAttributeValue();
        } catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
    }

    private String type;

    public AbstractChartsBuilder(String type) {
        this.type = type;
    }

    @Override
    public Collection<TemplateUnit> getTemplates() {
        return RESOURCES.get(type).values();
    }

    @Override
    public TemplateUnit getTemplate(String name) {
        if (Strings.isBlank(name))
            return null;
        if (RESOURCES.isEmpty())
            init();
        return RESOURCES.get(type).get(name);
    }

    @Override
    public Map<String, List> getCondition() {
        if (condition==null)
            init();
        return condition;
    }

    @Override
    public String build(Option option, TemplateUnit item, boolean clean) {
        String rs = null;
        if (item != null) {
            if (clean) {//clean data
                EChartsUtils.clear(option);
                rs = GsonUtil.format(option);
            } else
                rs = item.getOption();
            if (!Strings.isBlank(item.getJson())) {
                JsonElement json = GsonUtil.fromJson(item.getJson(), JsonElement.class);
                if (json instanceof JsonObject) {
                    JsonObject setting = (JsonObject) json;
                    JsonArray index = (JsonArray) setting.get("index");
                    JsonElement obj = setting.get("data");
                    if (index.size() == 1) {
                        if (option.getDataset() != null) {
                            option.getDataset().setSource(obj);
                        } else
                            ((Map) ((List) option.getSeries()).get(0)).put("data", obj);
                    } else {
                        boolean useArray = false;
                        boolean useObject = false;
                        for (int i = 0; i < index.size(); i++) {
                            String name = index.get(i).getAsString();
                            if (name.endsWith("]")) {
                                useArray = true;
                                break;
                            } else if (name.indexOf(".") > 0) {
                                useObject = true;
                                break;
                            }
                        }
                        int seriesIdx = 0, xAxisIdx = 0, yAxisIdx = 0;
                        if (useObject) {
                            JsonObject data = (JsonObject) obj;
                            for (int i = 0; i < index.size(); i++) {
                                String name = index.get(i).getAsString();
                                String field = StringUtils.substringAfter(name, ".");
                                name = StringUtils.substringBefore(name, ".");
                                if (name.equals("xAxis")) {
                                    xAxisIdx = setData(option.getxAxis(), data.get(field), xAxisIdx);
                                } else if (name.equals("yAxis")) {
                                    yAxisIdx = setData(option.getyAxis(), data.get(field), yAxisIdx);
                                } else if (name.equals("series")) {
                                    seriesIdx = setData(option.getSeries(), data.get(field), seriesIdx);
                                } else if (name.equals("angleAxis")) {
                                    option.getAngleAxis().setData(data.get(field));
                                } else if (name.equals("radiusAxis")) {
                                    option.getRadiusAxis().setData(data.get(field));
                                }
                            }
                        } else {
                            JsonArray data = (JsonArray) obj;
                            if (useArray) {
                                for (int i = 0; i < index.size(); i++) {
                                    String name = index.get(i).getAsString();
                                    int idx = Integer.parseInt(StringUtils.substringBetween(name, "[", "]"));
                                    name = StringUtils.substringBefore(name, "[");
                                    Object[] detail = new Object[data.size()];
                                    for (int j = 0; j < data.size(); j++)
                                        detail[j] = data.get(j).getAsJsonArray().get(idx);
                                    if (name.equals("xAxis")) {
                                        xAxisIdx = setData(option.getxAxis(), detail, xAxisIdx);
                                        if (xAxisIdx < 0)
                                            break;
                                    } else if (name.equals("yAxis")) {
                                        yAxisIdx = setData(option.getyAxis(), detail, yAxisIdx);
                                        if (yAxisIdx < 0)
                                            break;
                                    } else if (name.equals("series")) {
                                        seriesIdx = setData(option.getSeries(), detail, seriesIdx);
                                        if (seriesIdx < 0)
                                            break;
                                    } else if (name.equals("angleAxis")) {
                                        option.getAngleAxis().setData(detail);
                                    } else if (name.equals("radiusAxis")) {
                                        option.getRadiusAxis().setData(detail);
                                    }
                                }
                            } else {
                                for (int i = 0; i < index.size(); i++) {
                                    String name = index.get(i).getAsString();
                                    if (name.startsWith("xAxis")) {
                                        xAxisIdx = setData(option.getxAxis(), data.get(i), xAxisIdx);
                                        if (xAxisIdx < 0)
                                            break;
                                    } else if (name.startsWith("yAxis")) {
                                        yAxisIdx = setData(option.getyAxis(), data.get(i), yAxisIdx);
                                        if (yAxisIdx < 0)
                                            break;
                                    } else if (name.startsWith("series")) {
                                        seriesIdx = setData(option.getSeries(), data.get(i), seriesIdx);
                                        if (seriesIdx < 0)
                                            break;
                                    } else if (name.equals("angleAxis")) {
                                        option.getAngleAxis().setData(data.get(i));
                                    } else if (name.equals("radiusAxis")) {
                                        option.getRadiusAxis().setData(data.get(i));
                                    }
                                }
                            }
                        }
                    }
                } else if (json instanceof JsonArray)
                    setSeries(option, item.getName(), (JsonArray) json);
            }
        } else
            rs = GsonUtil.format(option);
        return rs;
    }

    int setData(Object option, Object data, int index) {
        if (option instanceof Map) {
            Map<String, Object> map = (Map) option;
            if (map.containsKey("data"))
                map.put("data", data);
        } else if (option instanceof List) {
            List list = (List) option;
            if (index >= list.size())
                return -1;
            Map<String, Object> map = (Map) list.get(index);
            if (map.containsKey("data"))
                map.put("data", data);
            else
                return -1;
            index++;
        }
        return index;
    }

    protected void setSeries(Option option, String name, JsonArray data) {
        if (option.getDataset() != null) {
            option.getDataset().setSource(data);
        } else if (option.getSeries() != null) {
            List series = (List) option.getSeries();
            if (series.size() == 1) {
                ((Map) series.get(0)).put("data", data);
            } else {
                JsonArray array = data;
                for (int i = 0; i < array.size(); i++)
                    ((Map) series.get(i)).put("data", array.get(i));
            }
        }
    }

    @Override
    public void build(ECharts charts, ComponentHandler dataHandler) {
        if (dataHandler == null) {
            build(charts.getOption(), getTemplate(charts.getTemplate()), true);
        } else if (charts.getOption().getDataset() != null) {
            createDataset(charts, dataHandler);
        }
    }

    protected void createDataset(ECharts echarts, ComponentHandler dataHandler) {
        Dataset ds = new Dataset();
        if (echarts.getOption().getDataset().getDimensions() != null) {
            if (echarts.getOption().getDataset().getDimensions() instanceof String)
                ds.setDimensions(dataHandler.selectOne(Object[].class, echarts.getDbId(), (String) echarts.getOption().getDataset().getDimensions()));
            else
                ds.dimensions(echarts.getOption().getDataset().getDimensions());
            if ("data".equals(echarts.getOption().getDataset().getSource()))
                ds.setSource(dataHandler.selectList0(Object[].class, echarts.getDbId(), (String) echarts.getQuery()));
        } else {
            List<Map> data = dataHandler.selectList0(Map.class, echarts.getDbId(), (String) echarts.getQuery());
            if (!data.isEmpty()) {
                List<Object[]> sources = new ArrayList<>();
                sources.add(data.get(0).keySet().toArray());
                for (Map objs : data)
                    sources.add(objs.values().toArray());
                ds.source(sources);
            }
        }
        echarts.getOption().setDataset(ds);
    }

/*
    protected Object createSeries(String type) {
        ChartType tp = ChartType.valueOf(type);
        switch (tp) {
            case line:
                return new Line();
            case bar:
                return new Bar();
            case pie:
                return new Pie();
            case scatter:
                return new Scatter();
            case effectScatter:
                return new EffectScatter();
            case radar:
                return new Radar();
            case tree:
                return new Tree();
            case treemap:
                return new Treemap();
            case sunburst:
                return new Sunburst();
            case boxplot:
                return new Boxplot();
            case candlestick:
                return new Candlestick();
            case heatmap:
                return new Heatmap();
            case map:
                return new cn.easyplatform.web.ext.echarts.lib.series.Map();
            case parallel:
                return new Parallel();
            case lines:
                return new Lines();
            case graph:
                return new Graph();
            case sankey:
                return new Sankey();
            case funnel:
                return new Funnel();
            case gauge:
                return new Gauge();
            case pictorialBar:
                return new PictorialBar();
            case themeRiver:
                return new ThemeRiver();
            case calendar:
                return new Calendar();
            default:
                return null;
        }
    }

    protected void createModel(ECharts echarts, Object[] model) {
        ChartType tp = ChartType.valueOf(echarts.getType());
        for (Object row : model) {
            switch (tp) {
                case line:
                    echarts.series(new Line().data(row));
                    break;
                case bar:
                    echarts.series(new Bar().data(row));
                    break;
                case pie:
                    echarts.series(new Pie().data(row));
                    break;
                case scatter:
                    echarts.series(new Scatter().data(row));
                    break;
                case radar:
                    echarts.series(new Radar().data(row));
                    break;
                case tree:

                    break;
                case treemap:
                    echarts.series(new Treemap().data(row));
                    break;
                case sunburst:

                    break;
                case boxplot:
                    echarts.series(new Boxplot().data(row));
                    break;
                case candlestick:
                    echarts.series(new Candlestick().data(row));
                    break;
                case heatmap:
                    echarts.series(new Heatmap().data(row));
                    break;
                case map:
                    echarts.series(new cn.easyplatform.web.ext.echarts.lib.series.Map().data(row));
                    break;
                case parallel:
                    echarts.series(new Parallel().data(row));
                    break;
                case lines:
                    echarts.series(new Lines().data(row));
                    break;
                case graph:
                    echarts.series(new Graph().data(row));
                    break;
                case sankey:
                    echarts.series(new Sankey().data(row));
                    break;
                case funnel:
                    echarts.series(new Funnel().data(row));
                    break;
                case gauge:
                    echarts.series(new Gauge().data(row));
                    break;
                case pictorialBar:
                    echarts.series(new PictorialBar().data(row));
                    break;
                case themeRiver:
                    echarts.series(new ThemeRiver().data(row));
                    break;
                case calendar:

                    break;
                default:

            }
        }

    }*/
}